#include "../exercise.h"
#include <utility>

// READ: 左值右值（概念）<https://learn.microsoft.com/zh-cn/cpp/c-language/l-value-and-r-value-expressions?view=msvc-170>
// READ: 左值右值（细节）<https://zh.cppreference.com/w/cpp/language/value_category>
// READ: 关于移动语义 <https://learn.microsoft.com/zh-cn/cpp/cpp/rvalue-reference-declarator-amp-amp?view=msvc-170#move-semantics>
// READ: 如果实现移动构造 <https://learn.microsoft.com/zh-cn/cpp/cpp/move-constructors-and-move-assignment-operators-cpp?view=msvc-170>

// READ: 移动构造函数 <https://zh.cppreference.com/w/cpp/language/move_constructor>
// READ: 移动赋值 <https://zh.cppreference.com/w/cpp/language/move_assignment>
// READ: 运算符重载 <https://zh.cppreference.com/w/cpp/language/operators>

// READ: 左值右值详解 <https://lb3fn675fh.feishu.cn/wiki/XdiOw2Wzqi7EFSkq0ZQcQkUAnVd>

class DynFibonacci {
    size_t *cache;
    int cached;
    int capacity;

public:
    // TODO: 实现动态设置容量的构造器
    DynFibonacci(int capacity) 
        : cache(new size_t[capacity]), cached(0), capacity(capacity) {
        // 初始化基础值
        if (capacity > 0) {
            cache[0] = 0;
            cached = 1;
        }
        if (capacity > 1) {
            cache[1] = 1;
            cached = 2;
        }
    }

    // TODO: 实现移动构造器
    DynFibonacci(DynFibonacci &&other) noexcept 
        : cache(std::exchange(other.cache, nullptr)),
          cached(std::exchange(other.cached, 0)),
          capacity(std::exchange(other.capacity, 0)) {}

    // TODO: 实现移动赋值
    // NOTICE: ⚠ 注意移动到自身问题 ⚠
    DynFibonacci &operator=(DynFibonacci &&other) noexcept {
        // 检查自赋值
        if (this != &other) {
            // 释放当前资源
            delete[] cache;
            
            // 窃取对方资源
            cache = std::exchange(other.cache, nullptr);
            cached = std::exchange(other.cached, 0);
            capacity = std::exchange(other.capacity, 0);
        }
        return *this;
    }


    // TODO: 实现析构器，释放缓存空间
    ~DynFibonacci() {
        delete[] cache;
    }

    // TODO: 实现正确的缓存优化斐波那契计算
    size_t operator[](int i) {
        // 边界检查
        if (i < 0 || i >= capacity) {
            ASSERT(false, "i out of range");
            return 0;
        }
        
        // 如果请求的索引已经在缓存中，直接返回
        if (i < cached) {
            return cache[i];
        }
        
        // 计算并缓存缺失的值
        for (int j = cached; j <= i; ++j) {
            if (j == 0) {
                cache[j] = 0;
            } else if (j == 1) {
                cache[j] = 1;
            } else {
                cache[j] = cache[j - 1] + cache[j - 2];
            }
        }
        
        // 更新已缓存的数量
        cached = i + 1;
        
        return cache[i];
    }

    // NOTICE: 不要修改这个方法
    size_t operator[](int i) const {
        ASSERT(i <= cached, "i out of range");
        return cache[i];
    }

    // NOTICE: 不要修改这个方法
    bool is_alive() const {
        return cache;
    }
};

int main(int argc, char **argv) {
    DynFibonacci fib(12);
    ASSERT(fib[10] == 55, "fibonacci(10) should be 55");

    DynFibonacci const fib_ = std::move(fib);
    ASSERT(!fib.is_alive(), "Object moved");
    ASSERT(fib_[10] == 55, "fibonacci(10) should be 55");

    DynFibonacci fib0(6);
    DynFibonacci fib1(12);

    fib0 = std::move(fib1);
    fib0 = std::move(fib0);
    ASSERT(fib0[10] == 55, "fibonacci(10) should be 55");

    return 0;
}

/*
左值、右值：
移动语义、移动构造：

移动构造函数：    
    DynFibonacci(DynFibonacci &&other) noexcept 
        : cache(std::exchange(other.cache, nullptr)),
          cached(std::exchange(other.cached, 0)),
          capacity(std::exchange(other.capacity, 0)) {}

传入右值引用，移动构造函数使用noexcept作用：
使用exchange进行转移：转移指针、转移成员变量；语法？

移动赋值=：
    DynFibonacci &operator=(DynFibonacci &&other) noexcept {
        // 检查自赋值
        if (this != &other) {
            // 释放当前资源
            delete[] cache;
            
            // 窃取对方资源
            cache = std::exchange(other.cache, nullptr);
            cached = std::exchange(other.cached, 0);
            capacity = std::exchange(other.capacity, 0);
        }
        return *this;
    }

判断不是赋值自己
然后其他和移动构造差不多
DynFibonacci &operator=(DynFibonacci &&other) noexcept
为什么有一个&？


ai:
移动语法分析：
资源窃取：使用std::exchange将源对象的资源转移过来
源对象置空：将源对象的指针置为nullptr，使其进入可析构状态
noexcept：标记为不会抛出异常，便于优化

自赋值检查：防止x = std::move(x)导致资源丢失
先释放后窃取：先释放当前资源，再窃取对方资源
源对象置空：确保源对象可以被安全析构

std::exchange的作用：
cache = std::exchange(other.cache, nullptr);
相当于：
size_t *temp = other.cache;
other.cache = nullptr;
cache = temp;


使用operator[]提供类似数组的访问语法，更加直观。

移动语义的优势
性能提升：避免不必要的深拷贝
资源转移：适合管理大型资源（如动态数组、文件句柄等）
标准库兼容：许多标准库容器和算法依赖移动语义进行优化


这里的 DynFibonacci & 表示这个运算符重载函数返回一个对当前对象（*this）的左值引用。

为什么需要返回引用？
赋值运算符通常需要支持链式赋值，比如：

a = b = c;  // 等价于 a = (b = c);
为了实现这种链式操作，赋值运算符需要返回对左侧对象的引用，这样右侧的表达式结果可以作为下一个赋值操作的右值。

如果返回的是值而不是引用：
DynFibonacci operator=(DynFibonacci &&other) noexcept
那么：
性能损失：每次赋值都会产生一次额外的拷贝/移动
语义错误：a = b = c 不会按预期工作
不符合惯例：标准库和所有C++代码都期望赋值运算符返回引用


上一题：
class DynFibonacci {
    size_t *cache;
    int cached;
    int capacity;

public:
    // TODO: 实现动态设置容量的构造器
    DynFibonacci(int capacity) 
        : cache(new size_t[capacity]), cached(0), capacity(capacity) {
        // 初始化基础值
        if (capacity > 0) {
            cache[0] = 0;
            cached = 1;
        }
        if (capacity > 1) {
            cache[1] = 1;
            cached = 2;
        }
    }

    // TODO: 实现复制构造器
    DynFibonacci(DynFibonacci const &other) 
        : cache(new size_t[other.capacity]), 
          cached(other.cached), 
          capacity(other.capacity) {
        // 复制缓存数据
        for (int i = 0; i < cached; ++i) {
            cache[i] = other.cache[i];
        }
    }

    // TODO: 实现析构器，释放缓存空间
    ~DynFibonacci() {
        delete[] cache;
    }

    // TODO: 实现正确的缓存优化斐波那契计算
    size_t get(int i) {
        // 边界检查
        if (i < 0 || i >= capacity) {
            ASSERT(false, "i out of range");
            return 0;
        }
        
        // 如果请求的索引已经在缓存中，直接返回
        if (i < cached) {
            return cache[i];
        }
        
        // 计算并缓存缺失的值
        for (int j = cached; j <= i; ++j) {
            if (j == 0) {
                cache[j] = 0;
            } else if (j == 1) {
                cache[j] = 1;
            } else {
                cache[j] = cache[j - 1] + cache[j - 2];
            }
        }
        
        // 更新已缓存的数量
        cached = i + 1;
        
        return cache[i];
    }

    // NOTICE: 不要修改这个方法
    // NOTICE: 名字相同参数也相同，但 const 修饰不同的方法是一对重载方法，可以同时存在
    //         本质上，方法是隐藏了 this 参数的函数
    //         const 修饰作用在 this 上，因此它们实际上参数不同
    size_t get(int i) const {
        if (i <= cached) {
            return cache[i];
        }
        ASSERT(false, "i out of range");
    }
};
*/